#include "U2EGLWindow.h"


#include "U2Exception.h"
#include "U2LogManager.h"
#include "U2StringConverter.h"
#include "U2GLESPixelUtil.h"
#include "U2EGLSupport.h"
#include "U2EGLContext.h"
#include "U2Root.h"
#include "U2WindowEventUtilities.h"
// #include "OgreGLESRenderSystem.h"




U2EG_NAMESPACE_USING


U2EGLWindow::U2EGLWindow(U2EGLSupport *glsupport)
: mGLSupport(glsupport)
, mContext(0)
, mEglConfig(0)
, mEglSurface(0)
, mWindow(0)
, mNativeDisplay(0)
, mEglDisplay(EGL_NO_DISPLAY)
{
    mIsTopLevel = false;
    mIsFullScreen = false;
    mClosed = false;
    mActive = true;//todo
    mIsExternalGLControl = false;
	mVisible = false;
}

U2EGLWindow::~U2EGLWindow()
{
    destroy();

    if (mContext)
    {
        delete mContext;
    }

    mContext = 0;
}

void U2EGLWindow::destroy(void)
{
    if (mClosed)
    {
        return;
    }

    mClosed = true;
    mActive = false;

    if (!mIsExternal)
    {
        U2WindowEventUtilities::_removeRenderWindow(this);
    }

    if (mIsFullScreen)
    {
        mGLSupport->switchMode();
        switchFullScreen(false);
    }
}

void U2EGLWindow::setFullscreen(bool fullscreen, u2uint width, u2uint height)
{
    short frequency = 0;

    if (mClosed || !mIsTopLevel)
    {
        return;
    }

    if (fullscreen == mIsFullScreen && width == mWidth && height == mHeight)
    {
        return;
    }
	if (fullscreen)
    {
        mGLSupport->switchMode(width, height, frequency);
    }
    else
    {
        mGLSupport->switchMode();
    }

    if (mIsFullScreen != fullscreen)
    {
        switchFullScreen(fullscreen);
    }

    if (!mIsFullScreen)
    {
        resize(width, height);
        reposition(mLeft, mTop);
    }
}

bool U2EGLWindow::isClosed() const
{
    return mClosed;
}

bool U2EGLWindow::isVisible() const
{
    return mVisible;
}

void U2EGLWindow::setVisible(bool visible)
{
    mVisible = visible;
}



void U2EGLWindow::swapBuffers(bool waitForVSync)
{
    if (mClosed || mIsExternalGLControl)
    {
        return;
    }

    glFlush();
    if (eglSwapBuffers(mEglDisplay, mEglSurface) == EGL_FALSE)
    {
        U2_EXCEPT(U2Exception::ERR_RENDERINGAPI_ERROR,
                    "Fail to SwapBuffers",
                    __FUNCTION__);
    }
}

void U2EGLWindow::getCustomAttribute(const U2String& name, void* pData)
{
    if (name == "DISPLAYNAME")
    {
        *static_cast<U2String*>(pData) = mGLSupport->getDisplayName();
        return;
    }
    else if (name == "DISPLAY")
    {
        *static_cast<EGLDisplay*>(pData) = mEglDisplay;
        return;
    }
    else if (name == "GLCONTEXT")
    {
        *static_cast<U2EGLContext**>(pData) = mContext;
        return;
    } 
	else if (name == "WINDOW")
	{
		*static_cast<NativeWindowType*>(pData) = mWindow;
		return;
	} 
}

void U2EGLWindow::copyContentsToMemory(const U2PixelBox &dst, FrameBuffer buffer)
{
    /**********************@@@@@@@@@@@@@@@@@@@@@
	if ((dst.left < 0) || (dst.right > mWidth) ||
		(dst.top < 0) || (dst.bottom > mHeight) ||
		(dst.front != 0) || (dst.back != 1))
	{
		U2_EXCEPT(U2Exception::ERR_INVALIDPARAMS,
			"Invalid box.",
			"Win32Window::copyContentsToMemory" );
	}

	if (buffer == FB_AUTO)
	{
		buffer = mIsFullScreen? FB_FRONT : FB_BACK;
	}

	GLenum format = U2GLESPixelUtil::getGLOriginFormat(dst.format);
	GLenum type = U2GLESPixelUtil::getGLOriginDataType(dst.format);

	if ((format == 0) || (type == 0))
	{
		U2_EXCEPT(U2Exception::ERR_INVALIDPARAMS,
			"Unsupported format.",
			"GtkEGLWindow::copyContentsToMemory" );
	}


	// Switch context if different from current one
	U2RenderSystem* rsys = U2Root::getSingleton().getRenderSystem();
	rsys->_setViewport(this->getViewport(0));

	// Must change the packing to ensure no overruns!
	glPixelStorei(GL_PACK_ALIGNMENT, 1);

	//glReadBuffer((buffer == FB_FRONT)? GL_FRONT : GL_BACK);
	glReadPixels((GLint)dst.left, (GLint)dst.top,
		(GLsizei)dst.getWidth(), (GLsizei)dst.getHeight(),
		format, type, dst.data);

	// restore default alignment
	glPixelStorei(GL_PACK_ALIGNMENT, 4);

	//vertical flip
	{
		size_t rowSpan = dst.getWidth() * U2PixelUtil::getNumElemBytes(dst.format);
		size_t height = dst.getHeight();
		u2uchar *tmpData = new u2uchar[rowSpan * height];
		u2uchar *srcRow = (u2uchar *)dst.data, *tmpRow = tmpData + (height - 1) * rowSpan;

		while (tmpRow >= tmpData)
		{
			memcpy(tmpRow, srcRow, rowSpan);
			srcRow += rowSpan;
			tmpRow -= rowSpan;
		}
		memcpy(dst.data, tmpData, rowSpan * height);

		delete [] tmpData;
	}
    */
}


::EGLSurface U2EGLWindow::createSurfaceFromWindow(::EGLDisplay display,
                                                NativeWindowType win)
{
    ::EGLSurface surface;

    surface = eglCreateWindowSurface(display, mEglConfig, win, NULL);

    if (surface == EGL_NO_SURFACE)
    {
        U2_EXCEPT(U2Exception::ERR_RENDERINGAPI_ERROR,
                    "Fail to create EGLSurface based on X NativeWindowType",
                    __FUNCTION__);
    }
    return surface;
}

bool U2EGLWindow::requiresTextureFlipping() const
{
	return false;
}

